2 ==============================================================================
\r
4 This file is part of the JUCE library.
\r
5 Copyright (c) 2017 - ROLI Ltd.
\r
7 JUCE is an open source library subject to commercial or open-source
\r
10 The code included in this file is provided under the terms of the ISC license
\r
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
\r
12 To use, copy, modify, and/or distribute this software for any purpose with or
\r
13 without fee is hereby granted provided that the above copyright notice and
\r
14 this permission notice appear in all copies.
\r
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
\r
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
\r
20 ==============================================================================
\r
23 package com.juce.networkgraphicsdemo;
\r
25 import android.app.Activity;
\r
26 import android.app.AlertDialog;
\r
27 import android.content.DialogInterface;
\r
28 import android.content.Context;
\r
29 import android.content.Intent;
\r
30 import android.content.res.Configuration;
\r
31 import android.content.pm.PackageInfo;
\r
32 import android.content.pm.PackageManager;
\r
33 import android.net.http.SslError;
\r
34 import android.net.Uri;
\r
35 import android.os.Bundle;
\r
36 import android.os.Looper;
\r
37 import android.os.Handler;
\r
38 import android.os.Message;
\r
39 import android.os.ParcelUuid;
\r
40 import android.os.Environment;
\r
41 import android.view.*;
\r
42 import android.view.inputmethod.BaseInputConnection;
\r
43 import android.view.inputmethod.EditorInfo;
\r
44 import android.view.inputmethod.InputConnection;
\r
45 import android.view.inputmethod.InputMethodManager;
\r
46 import android.graphics.*;
\r
47 import android.text.ClipboardManager;
\r
48 import android.text.InputType;
\r
49 import android.util.DisplayMetrics;
\r
50 import android.util.Log;
\r
51 import android.util.Pair;
\r
52 import android.webkit.SslErrorHandler;
\r
53 import android.webkit.WebChromeClient;
\r
54 import android.webkit.WebView;
\r
55 import android.webkit.WebViewClient;
\r
56 import java.lang.Runnable;
\r
57 import java.lang.ref.WeakReference;
\r
58 import java.lang.reflect.*;
\r
61 import java.net.URL;
\r
62 import java.net.HttpURLConnection;
\r
63 import android.media.AudioManager;
\r
64 import android.Manifest;
\r
65 import java.util.concurrent.CancellationException;
\r
66 import java.util.concurrent.Future;
\r
67 import java.util.concurrent.Executors;
\r
68 import java.util.concurrent.ExecutorService;
\r
69 import java.util.concurrent.ExecutionException;
\r
70 import java.util.concurrent.TimeUnit;
\r
71 import java.util.concurrent.Callable;
\r
72 import java.util.concurrent.TimeoutException;
\r
73 import java.util.concurrent.locks.ReentrantLock;
\r
74 import java.util.concurrent.atomic.*;
\r
78 //==============================================================================
\r
79 public class JUCENetworkGraphicsDemo extends Activity
\r
81 //==============================================================================
\r
84 System.loadLibrary ("juce_jni");
\r
87 //==============================================================================
\r
88 public boolean isPermissionDeclaredInManifest (int permissionID)
\r
90 return isPermissionDeclaredInManifest (getAndroidPermissionName (permissionID));
\r
93 public boolean isPermissionDeclaredInManifest (String permissionToCheck)
\r
97 PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
\r
99 if (info.requestedPermissions != null)
\r
100 for (String permission : info.requestedPermissions)
\r
101 if (permission.equals (permissionToCheck))
\r
104 catch (PackageManager.NameNotFoundException e)
\r
106 Log.d ("JUCE", "isPermissionDeclaredInManifest: PackageManager.NameNotFoundException = " + e.toString());
\r
109 Log.d ("JUCE", "isPermissionDeclaredInManifest: could not find requested permission " + permissionToCheck);
\r
113 //==============================================================================
\r
114 // these have to match the values of enum PermissionID in C++ class RuntimePermissions:
\r
115 private static final int JUCE_PERMISSIONS_RECORD_AUDIO = 1;
\r
116 private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2;
\r
117 private static final int JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE = 3;
\r
118 private static final int JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 4;
\r
119 private static final int JUCE_PERMISSIONS_CAMERA = 5;
\r
121 private static String getAndroidPermissionName (int permissionID)
\r
123 switch (permissionID)
\r
125 case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO;
\r
126 case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION;
\r
127 // use string value as this is not defined in SDKs < 16
\r
128 case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE";
\r
129 case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE;
\r
130 case JUCE_PERMISSIONS_CAMERA: return Manifest.permission.CAMERA;
\r
133 // unknown permission ID!
\r
135 return new String();
\r
138 public boolean isPermissionGranted (int permissionID)
\r
140 return getApplicationContext().checkCallingOrSelfPermission (getAndroidPermissionName (permissionID)) == PackageManager.PERMISSION_GRANTED;
\r
143 private Map<Integer, Long> permissionCallbackPtrMap;
\r
145 public void requestRuntimePermission (int permissionID, long ptrToCallback)
\r
147 String permissionName = getAndroidPermissionName (permissionID);
\r
149 if (getApplicationContext().checkCallingOrSelfPermission (permissionName) != PackageManager.PERMISSION_GRANTED)
\r
151 // remember callbackPtr, request permissions, and let onRequestPermissionResult call callback asynchronously
\r
152 permissionCallbackPtrMap.put (permissionID, ptrToCallback);
\r
153 requestPermissionsCompat (new String[]{permissionName}, permissionID);
\r
157 // permissions were already granted before, we can call callback directly
\r
158 androidRuntimePermissionsCallback (true, ptrToCallback);
\r
162 private native void androidRuntimePermissionsCallback (boolean permissionWasGranted, long ptrToCallback);
\r
165 //==============================================================================
\r
166 public interface JuceMidiPort
\r
168 boolean isInputPort();
\r
170 // start, stop does nothing on an output port
\r
176 // send will do nothing on an input port
\r
177 void sendMidi (byte[] msg, int offset, int count);
\r
180 //==============================================================================
\r
181 //==============================================================================
\r
182 public class BluetoothManager
\r
188 public String[] getMidiBluetoothAddresses()
\r
190 String[] bluetoothAddresses = new String[0];
\r
191 return bluetoothAddresses;
\r
194 public String getHumanReadableStringForBluetoothAddress (String address)
\r
199 public int getBluetoothDeviceStatus (String address)
\r
204 public void startStopScan (boolean shouldStart)
\r
208 public boolean pairBluetoothMidiDevice(String address)
\r
213 public void unpairBluetoothMidiDevice (String address)
\r
218 //==============================================================================
\r
219 public class MidiDeviceManager
\r
221 public MidiDeviceManager()
\r
225 public String[] getJuceAndroidMidiInputDevices()
\r
227 return new String[0];
\r
230 public String[] getJuceAndroidMidiOutputDevices()
\r
232 return new String[0];
\r
235 public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
\r
240 public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
\r
245 public String getInputPortNameForJuceIndex (int index)
\r
250 public String getOutputPortNameForJuceIndex (int index)
\r
257 public MidiDeviceManager getAndroidMidiDeviceManager()
\r
262 public BluetoothManager getAndroidBluetoothManager()
\r
267 //==============================================================================
\r
269 public void onCreate (Bundle savedInstanceState)
\r
271 super.onCreate (savedInstanceState);
\r
273 isScreenSaverEnabled = true;
\r
275 viewHolder = new ViewHolder (this);
\r
276 setContentView (viewHolder);
\r
278 setVolumeControlStream (AudioManager.STREAM_MUSIC);
\r
280 permissionCallbackPtrMap = new HashMap<Integer, Long>();
\r
281 appPausedResumedListeners = new HashMap<Long, AppPausedResumedListener>();
\r
285 protected void onDestroy()
\r
294 protected void onPause()
\r
298 Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]);
\r
300 for (Long k : keys)
\r
301 appPausedResumedListeners.get (k).appPaused();
\r
305 Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down
\r
306 // openGL glitches when pausing/resuming apps..
\r
307 } catch (InterruptedException e) {}
\r
313 protected void onResume()
\r
318 Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]);
\r
320 for (Long k : keys)
\r
321 appPausedResumedListeners.get (k).appResumed();
\r
325 public void onConfigurationChanged (Configuration cfg)
\r
327 super.onConfigurationChanged (cfg);
\r
328 setContentView (viewHolder);
\r
331 private void callAppLauncher()
\r
333 launchApp (getApplicationInfo().publicSourceDir,
\r
334 getApplicationInfo().dataDir);
\r
337 // Need to override this as the default implementation always finishes the activity.
\r
339 public void onBackPressed()
\r
341 ComponentPeerView focusedView = getViewWithFocusOrDefaultView();
\r
343 if (focusedView == null)
\r
346 focusedView.backButtonPressed();
\r
349 private ComponentPeerView getViewWithFocusOrDefaultView()
\r
351 for (int i = 0; i < viewHolder.getChildCount(); ++i)
\r
353 if (viewHolder.getChildAt (i).hasFocus())
\r
354 return (ComponentPeerView) viewHolder.getChildAt (i);
\r
357 if (viewHolder.getChildCount() > 0)
\r
358 return (ComponentPeerView) viewHolder.getChildAt (0);
\r
363 //==============================================================================
\r
364 private void hideActionBar()
\r
366 // get "getActionBar" method
\r
367 java.lang.reflect.Method getActionBarMethod = null;
\r
370 getActionBarMethod = this.getClass().getMethod ("getActionBar");
\r
372 catch (SecurityException e) { return; }
\r
373 catch (NoSuchMethodException e) { return; }
\r
374 if (getActionBarMethod == null) return;
\r
376 // invoke "getActionBar" method
\r
377 Object actionBar = null;
\r
380 actionBar = getActionBarMethod.invoke (this);
\r
382 catch (java.lang.IllegalArgumentException e) { return; }
\r
383 catch (java.lang.IllegalAccessException e) { return; }
\r
384 catch (java.lang.reflect.InvocationTargetException e) { return; }
\r
385 if (actionBar == null) return;
\r
387 // get "hide" method
\r
388 java.lang.reflect.Method actionBarHideMethod = null;
\r
391 actionBarHideMethod = actionBar.getClass().getMethod ("hide");
\r
393 catch (SecurityException e) { return; }
\r
394 catch (NoSuchMethodException e) { return; }
\r
395 if (actionBarHideMethod == null) return;
\r
397 // invoke "hide" method
\r
400 actionBarHideMethod.invoke (actionBar);
\r
402 catch (java.lang.IllegalArgumentException e) {}
\r
403 catch (java.lang.IllegalAccessException e) {}
\r
404 catch (java.lang.reflect.InvocationTargetException e) {}
\r
407 void requestPermissionsCompat (String[] permissions, int requestCode)
\r
409 Method requestPermissionsMethod = null;
\r
412 requestPermissionsMethod = this.getClass().getMethod ("requestPermissions",
\r
413 String[].class, int.class);
\r
415 catch (SecurityException e) { return; }
\r
416 catch (NoSuchMethodException e) { return; }
\r
417 if (requestPermissionsMethod == null) return;
\r
421 requestPermissionsMethod.invoke (this, permissions, requestCode);
\r
423 catch (java.lang.IllegalArgumentException e) {}
\r
424 catch (java.lang.IllegalAccessException e) {}
\r
425 catch (java.lang.reflect.InvocationTargetException e) {}
\r
428 //==============================================================================
\r
429 private native void launchApp (String appFile, String appDataDir);
\r
430 private native void quitApp();
\r
431 private native void suspendApp();
\r
432 private native void resumeApp();
\r
433 private native void setScreenSize (int screenWidth, int screenHeight, int dpi);
\r
434 private native void appActivityResult (int requestCode, int resultCode, Intent data);
\r
435 private native void appNewIntent (Intent intent);
\r
437 //==============================================================================
\r
438 private ViewHolder viewHolder;
\r
439 private MidiDeviceManager midiDeviceManager = null;
\r
440 private BluetoothManager bluetoothManager = null;
\r
441 private boolean isScreenSaverEnabled;
\r
442 private java.util.Timer keepAliveTimer;
\r
444 public final ComponentPeerView createNewView (boolean opaque, long host)
\r
446 ComponentPeerView v = new ComponentPeerView (this, opaque, host);
\r
447 viewHolder.addView (v);
\r
448 addAppPausedResumedListener (v, host);
\r
452 public final void deleteView (ComponentPeerView view)
\r
454 removeAppPausedResumedListener (view, view.host);
\r
458 ViewGroup group = (ViewGroup) (view.getParent());
\r
461 group.removeView (view);
\r
464 public final void deleteNativeSurfaceView (NativeSurfaceView view)
\r
466 ViewGroup group = (ViewGroup) (view.getParent());
\r
469 group.removeView (view);
\r
472 final class ViewHolder extends ViewGroup
\r
474 public ViewHolder (Context context)
\r
477 setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS);
\r
478 setFocusable (false);
\r
481 protected final void onLayout (boolean changed, int left, int top, int right, int bottom)
\r
483 setScreenSize (getWidth(), getHeight(), getDPI());
\r
487 isFirstResize = false;
\r
492 private final int getDPI()
\r
494 DisplayMetrics metrics = new DisplayMetrics();
\r
495 getWindowManager().getDefaultDisplay().getMetrics (metrics);
\r
496 return metrics.densityDpi;
\r
499 private boolean isFirstResize = true;
\r
502 public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom)
\r
504 canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE);
\r
507 //==============================================================================
\r
508 public final void setScreenSaver (boolean enabled)
\r
510 if (isScreenSaverEnabled != enabled)
\r
512 isScreenSaverEnabled = enabled;
\r
514 if (keepAliveTimer != null)
\r
516 keepAliveTimer.cancel();
\r
517 keepAliveTimer = null;
\r
522 getWindow().clearFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
\r
526 getWindow().addFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
\r
528 // If no user input is received after about 3 seconds, the OS will lower the
\r
529 // task's priority, so this timer forces it to be kept active.
\r
530 keepAliveTimer = new java.util.Timer();
\r
532 keepAliveTimer.scheduleAtFixedRate (new TimerTask()
\r
537 android.app.Instrumentation instrumentation = new android.app.Instrumentation();
\r
541 instrumentation.sendKeyDownUpSync (KeyEvent.KEYCODE_UNKNOWN);
\r
543 catch (Exception e)
\r
552 public final boolean getScreenSaver()
\r
554 return isScreenSaverEnabled;
\r
557 //==============================================================================
\r
558 public final String getClipboardContent()
\r
560 ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
\r
562 CharSequence content = clipboard.getText();
\r
563 return content != null ? content.toString() : new String();
\r
566 public final void setClipboardContent (String newText)
\r
568 ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
\r
569 clipboard.setText (newText);
\r
572 //==============================================================================
\r
573 public final void showMessageBox (String title, String message, final long callback)
\r
575 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
576 builder.setTitle (title)
\r
577 .setMessage (message)
\r
578 .setCancelable (true)
\r
579 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
581 public void onCancel (DialogInterface dialog)
\r
583 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
586 .setPositiveButton ("OK", new DialogInterface.OnClickListener()
\r
588 public void onClick (DialogInterface dialog, int id)
\r
591 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
595 builder.create().show();
\r
598 public final void showOkCancelBox (String title, String message, final long callback,
\r
599 String okButtonText, String cancelButtonText)
\r
601 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
602 builder.setTitle (title)
\r
603 .setMessage (message)
\r
604 .setCancelable (true)
\r
605 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
607 public void onCancel (DialogInterface dialog)
\r
609 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
612 .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener()
\r
614 public void onClick (DialogInterface dialog, int id)
\r
617 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 1);
\r
620 .setNegativeButton (cancelButtonText.isEmpty() ? "Cancel" : cancelButtonText, new DialogInterface.OnClickListener()
\r
622 public void onClick (DialogInterface dialog, int id)
\r
625 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
629 builder.create().show();
\r
632 public final void showYesNoCancelBox (String title, String message, final long callback)
\r
634 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
635 builder.setTitle (title)
\r
636 .setMessage (message)
\r
637 .setCancelable (true)
\r
638 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
640 public void onCancel (DialogInterface dialog)
\r
642 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
645 .setPositiveButton ("Yes", new DialogInterface.OnClickListener()
\r
647 public void onClick (DialogInterface dialog, int id)
\r
650 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 1);
\r
653 .setNegativeButton ("No", new DialogInterface.OnClickListener()
\r
655 public void onClick (DialogInterface dialog, int id)
\r
658 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 2);
\r
661 .setNeutralButton ("Cancel", new DialogInterface.OnClickListener()
\r
663 public void onClick (DialogInterface dialog, int id)
\r
666 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
670 builder.create().show();
\r
673 public native void alertDismissed (long callback, int id);
\r
675 //==============================================================================
\r
676 public interface AppPausedResumedListener
\r
682 private Map<Long, AppPausedResumedListener> appPausedResumedListeners;
\r
684 public void addAppPausedResumedListener (AppPausedResumedListener l, long listenerHost)
\r
686 appPausedResumedListeners.put (new Long (listenerHost), l);
\r
689 public void removeAppPausedResumedListener (AppPausedResumedListener l, long listenerHost)
\r
691 appPausedResumedListeners.remove (new Long (listenerHost));
\r
694 //==============================================================================
\r
695 public final class ComponentPeerView extends ViewGroup
\r
696 implements View.OnFocusChangeListener, AppPausedResumedListener
\r
698 public ComponentPeerView (Context context, boolean opaque_, long host)
\r
702 setWillNotDraw (false);
\r
705 setFocusable (true);
\r
706 setFocusableInTouchMode (true);
\r
707 setOnFocusChangeListener (this);
\r
709 // swap red and blue colours to match internal opengl texture format
\r
710 ColorMatrix colorMatrix = new ColorMatrix();
\r
712 float[] colorTransform = { 0, 0, 1.0f, 0, 0,
\r
715 0, 0, 0, 1.0f, 0 };
\r
717 colorMatrix.set (colorTransform);
\r
718 paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix));
\r
720 java.lang.reflect.Method method = null;
\r
724 method = getClass().getMethod ("setLayerType", int.class, Paint.class);
\r
726 catch (SecurityException e) {}
\r
727 catch (NoSuchMethodException e) {}
\r
729 if (method != null)
\r
733 int layerTypeNone = 0;
\r
734 method.invoke (this, layerTypeNone, null);
\r
736 catch (java.lang.IllegalArgumentException e) {}
\r
737 catch (java.lang.IllegalAccessException e) {}
\r
738 catch (java.lang.reflect.InvocationTargetException e) {}
\r
742 //==============================================================================
\r
743 private native void handlePaint (long host, Canvas canvas, Paint paint);
\r
746 public void onDraw (Canvas canvas)
\r
751 handlePaint (host, canvas, paint);
\r
755 public boolean isOpaque()
\r
760 private boolean opaque;
\r
762 private Paint paint = new Paint();
\r
764 //==============================================================================
\r
765 private native void handleMouseDown (long host, int index, float x, float y, long time);
\r
766 private native void handleMouseDrag (long host, int index, float x, float y, long time);
\r
767 private native void handleMouseUp (long host, int index, float x, float y, long time);
\r
770 public boolean onTouchEvent (MotionEvent event)
\r
775 int action = event.getAction();
\r
776 long time = event.getEventTime();
\r
778 switch (action & MotionEvent.ACTION_MASK)
\r
780 case MotionEvent.ACTION_DOWN:
\r
781 handleMouseDown (host, event.getPointerId(0), event.getX(), event.getY(), time);
\r
784 case MotionEvent.ACTION_CANCEL:
\r
785 case MotionEvent.ACTION_UP:
\r
786 handleMouseUp (host, event.getPointerId(0), event.getX(), event.getY(), time);
\r
789 case MotionEvent.ACTION_MOVE:
\r
791 int n = event.getPointerCount();
\r
792 for (int i = 0; i < n; ++i)
\r
793 handleMouseDrag (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
798 case MotionEvent.ACTION_POINTER_UP:
\r
800 int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
\r
801 handleMouseUp (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
805 case MotionEvent.ACTION_POINTER_DOWN:
\r
807 int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
\r
808 handleMouseDown (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
819 //==============================================================================
\r
820 private native void handleKeyDown (long host, int keycode, int textchar);
\r
821 private native void handleKeyUp (long host, int keycode, int textchar);
\r
822 private native void handleBackButton (long host);
\r
823 private native void handleKeyboardHidden (long host);
\r
825 public void showKeyboard (String type)
\r
827 InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
\r
831 if (type.length() > 0)
\r
833 imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
\r
834 imm.setInputMethod (getWindowToken(), type);
\r
835 keyboardDismissListener.startListening();
\r
839 imm.hideSoftInputFromWindow (getWindowToken(), 0);
\r
840 keyboardDismissListener.stopListening();
\r
845 public void backButtonPressed()
\r
850 handleBackButton (host);
\r
854 public boolean onKeyDown (int keyCode, KeyEvent event)
\r
861 case KeyEvent.KEYCODE_VOLUME_UP:
\r
862 case KeyEvent.KEYCODE_VOLUME_DOWN:
\r
863 return super.onKeyDown (keyCode, event);
\r
864 case KeyEvent.KEYCODE_BACK:
\r
866 ((Activity) getContext()).onBackPressed();
\r
874 handleKeyDown (host, keyCode, event.getUnicodeChar());
\r
879 public boolean onKeyUp (int keyCode, KeyEvent event)
\r
884 handleKeyUp (host, keyCode, event.getUnicodeChar());
\r
889 public boolean onKeyMultiple (int keyCode, int count, KeyEvent event)
\r
894 if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE)
\r
895 return super.onKeyMultiple (keyCode, count, event);
\r
897 if (event.getCharacters() != null)
\r
899 int utf8Char = event.getCharacters().codePointAt (0);
\r
900 handleKeyDown (host, utf8Char, utf8Char);
\r
907 //==============================================================================
\r
908 private final class KeyboardDismissListener
\r
910 public KeyboardDismissListener (ComponentPeerView viewToUse)
\r
915 private void startListening()
\r
917 view.getViewTreeObserver().addOnGlobalLayoutListener(viewTreeObserver);
\r
920 private void stopListening()
\r
922 view.getViewTreeObserver().removeGlobalOnLayoutListener(viewTreeObserver);
\r
925 private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener
\r
929 keyboardShown = false;
\r
933 public void onGlobalLayout()
\r
935 Rect r = new Rect();
\r
937 ViewGroup parentView = (ViewGroup) getParent();
\r
939 if (parentView == null)
\r
942 parentView.getWindowVisibleDisplayFrame (r);
\r
944 int diff = parentView.getHeight() - (r.bottom - r.top);
\r
946 // Arbitrary threshold, surely keyboard would take more than 20 pix.
\r
947 if (diff < 20 && keyboardShown)
\r
949 keyboardShown = false;
\r
950 handleKeyboardHidden (view.host);
\r
953 if (! keyboardShown && diff > 20)
\r
954 keyboardShown = true;
\r
957 private boolean keyboardShown;
\r
960 private ComponentPeerView view;
\r
961 private TreeObserver viewTreeObserver = new TreeObserver();
\r
964 private KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener(this);
\r
966 // this is here to make keyboard entry work on a Galaxy Tab2 10.1
\r
968 public InputConnection onCreateInputConnection (EditorInfo outAttrs)
\r
970 outAttrs.actionLabel = "";
\r
971 outAttrs.hintText = "";
\r
972 outAttrs.initialCapsMode = 0;
\r
973 outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
\r
974 outAttrs.label = "";
\r
975 outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
\r
976 outAttrs.inputType = InputType.TYPE_NULL;
\r
978 return new BaseInputConnection (this, false);
\r
981 //==============================================================================
\r
983 protected void onSizeChanged (int w, int h, int oldw, int oldh)
\r
988 super.onSizeChanged (w, h, oldw, oldh);
\r
989 viewSizeChanged (host);
\r
993 protected void onLayout (boolean changed, int left, int top, int right, int bottom)
\r
995 for (int i = getChildCount(); --i >= 0;)
\r
996 requestTransparentRegion (getChildAt (i));
\r
999 private native void viewSizeChanged (long host);
\r
1002 public void onFocusChange (View v, boolean hasFocus)
\r
1008 focusChanged (host, hasFocus);
\r
1011 private native void focusChanged (long host, boolean hasFocus);
\r
1013 public void setViewName (String newName) {}
\r
1015 public void setSystemUiVisibilityCompat (int visibility)
\r
1017 Method systemUIVisibilityMethod = null;
\r
1020 systemUIVisibilityMethod = this.getClass().getMethod ("setSystemUiVisibility", int.class);
\r
1022 catch (SecurityException e) { return; }
\r
1023 catch (NoSuchMethodException e) { return; }
\r
1024 if (systemUIVisibilityMethod == null) return;
\r
1028 systemUIVisibilityMethod.invoke (this, visibility);
\r
1030 catch (java.lang.IllegalArgumentException e) {}
\r
1031 catch (java.lang.IllegalAccessException e) {}
\r
1032 catch (java.lang.reflect.InvocationTargetException e) {}
\r
1035 public boolean isVisible() { return getVisibility() == VISIBLE; }
\r
1036 public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); }
\r
1038 public boolean containsPoint (int x, int y)
\r
1040 return true; //xxx needs to check overlapping views
\r
1043 //==============================================================================
\r
1044 private native void handleAppPaused (long host);
\r
1045 private native void handleAppResumed (long host);
\r
1048 public void appPaused()
\r
1053 handleAppPaused (host);
\r
1057 public void appResumed()
\r
1062 // Ensure that navigation/status bar visibility is correctly restored.
\r
1063 handleAppResumed (host);
\r
1067 //==============================================================================
\r
1068 public static class NativeSurfaceView extends SurfaceView
\r
1069 implements SurfaceHolder.Callback
\r
1071 private long nativeContext = 0;
\r
1072 private boolean forVideo;
\r
1074 NativeSurfaceView (Context context, long nativeContextPtr, boolean createdForVideo)
\r
1077 nativeContext = nativeContextPtr;
\r
1078 forVideo = createdForVideo;
\r
1081 public Surface getNativeSurface()
\r
1083 Surface retval = null;
\r
1085 SurfaceHolder holder = getHolder();
\r
1086 if (holder != null)
\r
1087 retval = holder.getSurface();
\r
1092 //==============================================================================
\r
1094 public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
\r
1097 surfaceChangedNativeVideo (nativeContext, holder, format, width, height);
\r
1099 surfaceChangedNative (nativeContext, holder, format, width, height);
\r
1103 public void surfaceCreated (SurfaceHolder holder)
\r
1106 surfaceCreatedNativeVideo (nativeContext, holder);
\r
1108 surfaceCreatedNative (nativeContext, holder);
\r
1112 public void surfaceDestroyed (SurfaceHolder holder)
\r
1115 surfaceDestroyedNativeVideo (nativeContext, holder);
\r
1117 surfaceDestroyedNative (nativeContext, holder);
\r
1121 protected void dispatchDraw (Canvas canvas)
\r
1123 super.dispatchDraw (canvas);
\r
1126 dispatchDrawNativeVideo (nativeContext, canvas);
\r
1128 dispatchDrawNative (nativeContext, canvas);
\r
1131 //==============================================================================
\r
1133 protected void onAttachedToWindow()
\r
1135 super.onAttachedToWindow();
\r
1136 getHolder().addCallback (this);
\r
1140 protected void onDetachedFromWindow()
\r
1142 super.onDetachedFromWindow();
\r
1143 getHolder().removeCallback (this);
\r
1146 //==============================================================================
\r
1147 private native void dispatchDrawNative (long nativeContextPtr, Canvas canvas);
\r
1148 private native void surfaceCreatedNative (long nativeContextptr, SurfaceHolder holder);
\r
1149 private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
\r
1150 private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
\r
1151 int format, int width, int height);
\r
1153 private native void dispatchDrawNativeVideo (long nativeContextPtr, Canvas canvas);
\r
1154 private native void surfaceCreatedNativeVideo (long nativeContextptr, SurfaceHolder holder);
\r
1155 private native void surfaceDestroyedNativeVideo (long nativeContextptr, SurfaceHolder holder);
\r
1156 private native void surfaceChangedNativeVideo (long nativeContextptr, SurfaceHolder holder,
\r
1157 int format, int width, int height);
\r
1160 public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr, boolean forVideo)
\r
1162 return new NativeSurfaceView (this, nativeSurfacePtr, forVideo);
\r
1165 //==============================================================================
\r
1166 public final int[] renderGlyph (char glyph1, char glyph2, Paint paint, android.graphics.Matrix matrix, Rect bounds)
\r
1168 Path p = new Path();
\r
1170 char[] str = { glyph1, glyph2 };
\r
1171 paint.getTextPath (str, 0, (glyph2 != 0 ? 2 : 1), 0.0f, 0.0f, p);
\r
1173 RectF boundsF = new RectF();
\r
1174 p.computeBounds (boundsF, true);
\r
1175 matrix.mapRect (boundsF);
\r
1177 boundsF.roundOut (bounds);
\r
1181 final int w = bounds.width();
\r
1182 final int h = Math.max (1, bounds.height());
\r
1184 Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
\r
1186 Canvas c = new Canvas (bm);
\r
1187 matrix.postTranslate (-bounds.left, -bounds.top);
\r
1188 c.setMatrix (matrix);
\r
1189 c.drawPath (p, paint);
\r
1191 final int sizeNeeded = w * h;
\r
1192 if (cachedRenderArray.length < sizeNeeded)
\r
1193 cachedRenderArray = new int [sizeNeeded];
\r
1195 bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h);
\r
1197 return cachedRenderArray;
\r
1200 private int[] cachedRenderArray = new int [256];
\r
1202 //==============================================================================
\r
1203 public static class NativeInvocationHandler implements InvocationHandler
\r
1205 public NativeInvocationHandler (Activity activityToUse, long nativeContextRef)
\r
1207 activity = activityToUse;
\r
1208 nativeContext = nativeContextRef;
\r
1211 public void nativeContextDeleted()
\r
1213 nativeContext = 0;
\r
1217 public void finalize()
\r
1219 activity.runOnUiThread (new Runnable()
\r
1224 if (nativeContext != 0)
\r
1225 dispatchFinalize (nativeContext);
\r
1231 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable
\r
1233 return dispatchInvoke (nativeContext, proxy, method, args);
\r
1236 //==============================================================================
\r
1237 Activity activity;
\r
1238 private long nativeContext = 0;
\r
1240 private native void dispatchFinalize (long nativeContextRef);
\r
1241 private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args);
\r
1244 public InvocationHandler createInvocationHandler (long nativeContextRef)
\r
1246 return new NativeInvocationHandler (this, nativeContextRef);
\r
1249 public void invocationHandlerContextDeleted (InvocationHandler handler)
\r
1251 ((NativeInvocationHandler) handler).nativeContextDeleted();
\r
1254 //==============================================================================
\r
1255 public static class HTTPStream
\r
1257 public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse,
\r
1258 String headersToUse, int timeOutMsToUse,
\r
1259 int[] statusCodeToUse, StringBuffer responseHeadersToUse,
\r
1260 int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException
\r
1262 isPost = isPostToUse;
\r
1263 postData = postDataToUse;
\r
1264 headers = headersToUse;
\r
1265 timeOutMs = timeOutMsToUse;
\r
1266 statusCode = statusCodeToUse;
\r
1267 responseHeaders = responseHeadersToUse;
\r
1269 numRedirectsToFollow = numRedirectsToFollowToUse;
\r
1270 httpRequestCmd = httpRequestCmdToUse;
\r
1272 connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd);
\r
1275 private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData,
\r
1276 String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException
\r
1278 HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection());
\r
1282 newConnection.setInstanceFollowRedirects (false);
\r
1283 newConnection.setConnectTimeout (timeOutMs);
\r
1284 newConnection.setReadTimeout (timeOutMs);
\r
1286 // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines.
\r
1287 // So convert headers string to an array, with an element for each line
\r
1288 String headerLines[] = headers.split("\\n");
\r
1290 // Set request headers
\r
1291 for (int i = 0; i < headerLines.length; ++i)
\r
1293 int pos = headerLines[i].indexOf (":");
\r
1295 if (pos > 0 && pos < headerLines[i].length())
\r
1297 String field = headerLines[i].substring (0, pos);
\r
1298 String value = headerLines[i].substring (pos + 1);
\r
1300 if (value.length() > 0)
\r
1301 newConnection.setRequestProperty (field, value);
\r
1305 newConnection.setRequestMethod (httpRequestCmd);
\r
1309 newConnection.setDoOutput (true);
\r
1311 if (postData != null)
\r
1313 OutputStream out = newConnection.getOutputStream();
\r
1314 out.write(postData);
\r
1319 return newConnection;
\r
1321 catch (Throwable e)
\r
1323 newConnection.disconnect();
\r
1324 throw new IOException ("Connection error");
\r
1328 private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException
\r
1330 synchronized (createFutureLock)
\r
1332 if (hasBeenCancelled.get())
\r
1335 streamFuture = executor.submit (new Callable<BufferedInputStream>()
\r
1338 public BufferedInputStream call() throws IOException
\r
1340 return new BufferedInputStream (isInput ? connection.getInputStream()
\r
1341 : connection.getErrorStream());
\r
1348 return streamFuture.get();
\r
1350 catch (InterruptedException e)
\r
1354 catch (CancellationException e)
\r
1360 public final boolean connect()
\r
1362 boolean result = false;
\r
1363 int numFollowedRedirects = 0;
\r
1367 result = doConnect();
\r
1372 if (++numFollowedRedirects > numRedirectsToFollow)
\r
1375 int status = statusCode[0];
\r
1377 if (status == 301 || status == 302 || status == 303 || status == 307)
\r
1379 // Assumes only one occurrence of "Location"
\r
1380 int pos1 = responseHeaders.indexOf ("Location:") + 10;
\r
1381 int pos2 = responseHeaders.indexOf ("\n", pos1);
\r
1385 String currentLocation = connection.getURL().toString();
\r
1386 String newLocation = responseHeaders.substring (pos1, pos2);
\r
1390 // Handle newLocation whether it's absolute or relative
\r
1391 URL baseUrl = new URL (currentLocation);
\r
1392 URL newUrl = new URL (baseUrl, newLocation);
\r
1393 String transformedNewLocation = newUrl.toString();
\r
1395 if (transformedNewLocation != currentLocation)
\r
1397 // Clear responseHeaders before next iteration
\r
1398 responseHeaders.delete (0, responseHeaders.length());
\r
1400 synchronized (createStreamLock)
\r
1402 if (hasBeenCancelled.get())
\r
1405 connection.disconnect();
\r
1409 connection = createConnection (transformedNewLocation, isPost,
\r
1410 postData, headers, timeOutMs,
\r
1413 catch (Throwable e)
\r
1424 catch (Throwable e)
\r
1443 private final boolean doConnect()
\r
1445 synchronized (createStreamLock)
\r
1447 if (hasBeenCancelled.get())
\r
1454 inputStream = getCancellableStream (true);
\r
1456 catch (ExecutionException e)
\r
1458 if (connection.getResponseCode() < 400)
\r
1460 statusCode[0] = connection.getResponseCode();
\r
1461 connection.disconnect();
\r
1467 statusCode[0] = connection.getResponseCode();
\r
1472 if (statusCode[0] >= 400)
\r
1473 inputStream = getCancellableStream (false);
\r
1475 inputStream = getCancellableStream (true);
\r
1477 catch (ExecutionException e)
\r
1480 for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
\r
1482 if (entry.getKey() != null && entry.getValue() != null)
\r
1484 responseHeaders.append(entry.getKey() + ": "
\r
1485 + android.text.TextUtils.join(",", entry.getValue()) + "\n");
\r
1487 if (entry.getKey().compareTo ("Content-Length") == 0)
\r
1488 totalLength = Integer.decode (entry.getValue().get (0));
\r
1494 catch (IOException e)
\r
1501 static class DisconnectionRunnable implements Runnable
\r
1503 public DisconnectionRunnable (HttpURLConnection theConnection,
\r
1504 InputStream theInputStream,
\r
1505 ReentrantLock theCreateStreamLock,
\r
1506 Object theCreateFutureLock,
\r
1507 Future<BufferedInputStream> theStreamFuture)
\r
1509 connectionToDisconnect = theConnection;
\r
1510 inputStream = theInputStream;
\r
1511 createStreamLock = theCreateStreamLock;
\r
1512 createFutureLock = theCreateFutureLock;
\r
1513 streamFuture = theStreamFuture;
\r
1520 if (! createStreamLock.tryLock())
\r
1522 synchronized (createFutureLock)
\r
1524 if (streamFuture != null)
\r
1525 streamFuture.cancel (true);
\r
1528 createStreamLock.lock();
\r
1531 if (connectionToDisconnect != null)
\r
1532 connectionToDisconnect.disconnect();
\r
1534 if (inputStream != null)
\r
1535 inputStream.close();
\r
1537 catch (IOException e)
\r
1541 createStreamLock.unlock();
\r
1545 private HttpURLConnection connectionToDisconnect;
\r
1546 private InputStream inputStream;
\r
1547 private ReentrantLock createStreamLock;
\r
1548 private Object createFutureLock;
\r
1549 Future<BufferedInputStream> streamFuture;
\r
1552 public final void release()
\r
1554 DisconnectionRunnable disconnectionRunnable = new DisconnectionRunnable (connection,
\r
1560 synchronized (createStreamLock)
\r
1562 hasBeenCancelled.set (true);
\r
1564 connection = null;
\r
1567 Thread disconnectionThread = new Thread(disconnectionRunnable);
\r
1568 disconnectionThread.start();
\r
1571 public final int read (byte[] buffer, int numBytes)
\r
1577 synchronized (createStreamLock)
\r
1579 if (inputStream != null)
\r
1580 num = inputStream.read (buffer, 0, numBytes);
\r
1583 catch (IOException e)
\r
1592 public final long getPosition() { return position; }
\r
1593 public final long getTotalLength() { return totalLength; }
\r
1594 public final boolean isExhausted() { return false; }
\r
1595 public final boolean setPosition (long newPos) { return false; }
\r
1597 private boolean isPost;
\r
1598 private byte[] postData;
\r
1599 private String headers;
\r
1600 private int timeOutMs;
\r
1601 String httpRequestCmd;
\r
1602 private HttpURLConnection connection;
\r
1603 private int[] statusCode;
\r
1604 private StringBuffer responseHeaders;
\r
1605 private int totalLength;
\r
1606 private int numRedirectsToFollow;
\r
1607 private InputStream inputStream;
\r
1608 private long position;
\r
1609 private final ReentrantLock createStreamLock = new ReentrantLock();
\r
1610 private final Object createFutureLock = new Object();
\r
1611 private AtomicBoolean hasBeenCancelled = new AtomicBoolean();
\r
1613 private final ExecutorService executor = Executors.newCachedThreadPool (Executors.defaultThreadFactory());
\r
1614 Future<BufferedInputStream> streamFuture;
\r
1617 public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
\r
1618 String headers, int timeOutMs, int[] statusCode,
\r
1619 StringBuffer responseHeaders, int numRedirectsToFollow,
\r
1620 String httpRequestCmd)
\r
1622 // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
\r
1623 if (timeOutMs < 0)
\r
1625 else if (timeOutMs == 0)
\r
1626 timeOutMs = 30000;
\r
1632 HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers,
\r
1633 timeOutMs, statusCode, responseHeaders,
\r
1634 numRedirectsToFollow, httpRequestCmd);
\r
1636 return httpStream;
\r
1638 catch (Throwable e) {}
\r
1644 public final void launchURL (String url)
\r
1646 startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url)));
\r
1649 private native boolean webViewPageLoadStarted (long host, WebView view, String url);
\r
1650 private native void webViewPageLoadFinished (long host, WebView view, String url);
\r
1651 private native void webViewReceivedSslError (long host, WebView view, SslErrorHandler handler, SslError error);
\r
1652 private native void webViewCloseWindowRequest (long host, WebView view);
\r
1653 private native void webViewCreateWindowRequest (long host, WebView view);
\r
1655 //==============================================================================
\r
1656 public class JuceWebViewClient extends WebViewClient
\r
1658 public JuceWebViewClient (long hostToUse)
\r
1663 public void hostDeleted()
\r
1665 synchronized (hostLock)
\r
1672 public void onPageFinished (WebView view, String url)
\r
1677 webViewPageLoadFinished (host, view, url);
\r
1681 public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error)
\r
1686 webViewReceivedSslError (host, view, handler, error);
\r
1690 public void onPageStarted (WebView view, String url, Bitmap favicon)
\r
1693 webViewPageLoadStarted (host, view, url);
\r
1696 private long host;
\r
1697 private final Object hostLock = new Object();
\r
1700 public class JuceWebChromeClient extends WebChromeClient
\r
1702 public JuceWebChromeClient (long hostToUse)
\r
1708 public void onCloseWindow (WebView window)
\r
1710 webViewCloseWindowRequest (host, window);
\r
1714 public boolean onCreateWindow (WebView view, boolean isDialog,
\r
1715 boolean isUserGesture, Message resultMsg)
\r
1717 webViewCreateWindowRequest (host, view);
\r
1721 private long host;
\r
1722 private final Object hostLock = new Object();
\r
1726 //==============================================================================
\r
1727 public static final String getLocaleValue (boolean isRegion)
\r
1729 java.util.Locale locale = java.util.Locale.getDefault();
\r
1731 return isRegion ? locale.getCountry()
\r
1732 : locale.getLanguage();
\r
1735 private static final String getFileLocation (String type)
\r
1737 return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath();
\r
1740 public static final String getDocumentsFolder()
\r
1742 if (getAndroidSDKVersion() >= 19)
\r
1743 return getFileLocation ("Documents");
\r
1745 return Environment.getDataDirectory().getAbsolutePath();
\r
1748 public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); }
\r
1749 public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); }
\r
1750 public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); }
\r
1751 public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); }
\r
1753 //==============================================================================
\r
1755 protected void onActivityResult (int requestCode, int resultCode, Intent data)
\r
1757 appActivityResult (requestCode, resultCode, data);
\r
1761 protected void onNewIntent (Intent intent)
\r
1763 super.onNewIntent(intent);
\r
1764 setIntent(intent);
\r
1766 appNewIntent (intent);
\r
1769 //==============================================================================
\r
1770 public final Typeface getTypeFaceFromAsset (String assetName)
\r
1774 return Typeface.createFromAsset (this.getResources().getAssets(), assetName);
\r
1776 catch (Throwable e) {}
\r
1781 final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
\r
1783 public static String bytesToHex (byte[] bytes)
\r
1785 char[] hexChars = new char[bytes.length * 2];
\r
1787 for (int j = 0; j < bytes.length; ++j)
\r
1789 int v = bytes[j] & 0xff;
\r
1790 hexChars[j * 2] = hexArray[v >>> 4];
\r
1791 hexChars[j * 2 + 1] = hexArray[v & 0x0f];
\r
1794 return new String (hexChars);
\r
1797 final private java.util.Map dataCache = new java.util.HashMap();
\r
1799 synchronized private final File getDataCacheFile (byte[] data)
\r
1803 java.security.MessageDigest digest = java.security.MessageDigest.getInstance ("MD5");
\r
1804 digest.update (data);
\r
1806 String key = bytesToHex (digest.digest());
\r
1808 if (dataCache.containsKey (key))
\r
1809 return (File) dataCache.get (key);
\r
1811 File f = new File (this.getCacheDir(), "bindata_" + key);
\r
1813 FileOutputStream os = new FileOutputStream (f);
\r
1814 os.write (data, 0, data.length);
\r
1815 dataCache.put (key, f);
\r
1818 catch (Throwable e) {}
\r
1823 private final void clearDataCache()
\r
1825 java.util.Iterator it = dataCache.values().iterator();
\r
1827 while (it.hasNext())
\r
1829 File f = (File) it.next();
\r
1834 public final Typeface getTypeFaceFromByteArray (byte[] data)
\r
1838 File f = getDataCacheFile (data);
\r
1841 return Typeface.createFromFile (f);
\r
1843 catch (Exception e)
\r
1845 Log.e ("JUCE", e.toString());
\r
1851 public static final int getAndroidSDKVersion()
\r
1853 return android.os.Build.VERSION.SDK_INT;
\r
1856 public final String audioManagerGetProperty (String property)
\r
1858 Object obj = getSystemService (AUDIO_SERVICE);
\r
1862 java.lang.reflect.Method method;
\r
1866 method = obj.getClass().getMethod ("getProperty", String.class);
\r
1868 catch (SecurityException e) { return null; }
\r
1869 catch (NoSuchMethodException e) { return null; }
\r
1871 if (method == null)
\r
1876 return (String) method.invoke (obj, property);
\r
1878 catch (java.lang.IllegalArgumentException e) {}
\r
1879 catch (java.lang.IllegalAccessException e) {}
\r
1880 catch (java.lang.reflect.InvocationTargetException e) {}
\r
1885 public final boolean hasSystemFeature (String property)
\r
1887 return getPackageManager().hasSystemFeature (property);
\r